//! 结构体示例
//!
//! 结构体是 Rust 中一种自定义数据类型，允许你将相关数据打包在一起
//!
//! 结构体（structure，缩写成 struct）有 3 种类型，使用 struct 关键字来创建：
//! - 元组结构体（tuple struct），事实上就是具名元组而已。
//! - 经典的 C 语言风格结构体（C struct）。
//! - 单元结构体（unit struct），不带字段，在泛型中很有用。

/// 定义一个表示用户账户的结构体
///
/// 这是一个具名字段结构体（Struct with named fields）
///
/// 结构体可以作为另一个结构体的字段
#[derive(Debug)]
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

/// 创建结构体实例
#[test]
pub fn struct_basics() {
    // 创建 User 结构体的实例
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };

    println!("结构体示例:");
    println!("用户信息: {user1:?}");
    println!("用户名: {}", user1.username);
    println!("邮箱: {}", user1.email);

    // 创建可变结构体实例
    let mut user2 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };

    user2.email = String::from("anotheremail@example.com");
    println!("修改后的用户邮箱: {}", user1.email);

    // 使用结构体更新语法创建新的结构体，
    // 这样可以用到之前的结构体的字段
    let user3 = User {
        email: String::from("user3@example.com"),
        ..user2 // 使用 user2 的其余字段
    };
    println!("使用结构体更新语法创建的用户: {user3:?}");

    // 使用 `let` 绑定来解构结构体
    let User {
        username: user_name,
        email: user_email,
        sign_in_count: _, // 忽略这个字段
        active: _,        // 忽略这个字段
    } = user1;

    println!("解构得到的用户名: {user_name}, 邮箱: {user_email}");
}

/// 元组结构体
///
/// 元组结构体有名字，但字段没有名字
#[derive(Debug)]
struct Color(i32, i32, i32);
#[derive(Debug)]
struct Point(i32, i32, i32);

/// 带有两个字段的结构体
#[derive(Debug)]
struct RectanglePoint {
    x: f32,
    y: f32,
}

#[test]
pub fn tuple_structs() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);

    println!("元组结构体示例:");
    println!("黑色: RGB({}, {}, {})", black.0, black.1, black.2);
    println!("原点坐标: ({}, {})", origin.0, origin.1);

    // 实例化一个元组结构体
    let pair = Point(1, 0, 1);

    // 访问元组结构体的字段
    println!("pair contains {:?} and {:?}", pair.0, pair.1);

    // 解构一个元组结构体
    let Point(x, y, z) = pair;
    println!("解构后 pair contains {x:?} and {y:?} and {z:?}");

    let point: RectanglePoint = RectanglePoint { x: 10.3, y: 0.4 };

    // 访问 point 的字段
    println!("point coordinates: ({}, {})", point.x, point.y);

    // 使用结构体更新语法创建新的 point，
    // 这样可以用到之前的 point 的字段
    let bottom_right = RectanglePoint { x: 5.2, ..point };

    // `new_point.y` 与 `point.y` 一样，因为这个字段就是从 `point` 中来的
    println!("second point: ({}, {})", bottom_right.x, bottom_right.y);

    // 使用 `let` 绑定来解构 point
    let RectanglePoint { x: left_edge, y: top_edge } = point;
    let _rectangle = RectanglePoint { x: left_edge, y: top_edge };
}

/// 单元结构体
///
/// 单元结构体不包含任何字段，通常用于实现 trait
#[derive(Debug)]
struct AlwaysEqual;

#[test]
pub fn unit_structs() {
    let subject = AlwaysEqual;

    println!("单元结构体示例:");
    println!("创建了一个单元结构体实例: {subject:?}");

    // 实例化一个单元结构体
    let _unit = AlwaysEqual;
}

/// 结构体方法示例
///
/// 为结构体实现方法
///
/// `derive` 属性会自动创建所需的实现，使这个 `struct` 能使用 `fmt::Debug` 打印
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    /// 关联函数，用于创建新的 Rectangle 实例
    fn new(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }

    /// 计算面积的方法
    fn area(&self) -> u32 {
        self.width * self.height
    }

    /// 检查是否可以容纳另一个矩形
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }

    /// 修改矩形尺寸的方法
    fn set_dimensions(&mut self, width: u32, height: u32) {
        self.width = width;
        self.height = height;
    }
}

/// 实现 Display trait 用于格式化输出
use std::fmt;

impl fmt::Display for Rectangle {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Rectangle(width={},height={})", self.width, self.height)
    }
}

#[test]
pub fn struct_methods() {
    let rect1 = Rectangle::new(30, 50);
    let rect2 = Rectangle::new(10, 40);
    let rect3 = Rectangle::new(60, 45);

    println!("结构体方法示例:");
    println!("rect1 的面积: {}", rect1.area());
    println!("rect1 能容纳 rect2: {}", rect1.can_hold(&rect2));
    println!("rect1 能容纳 rect3: {}", rect1.can_hold(&rect3));

    // 使用可变引用修改结构体
    let mut rect4 = Rectangle::new(10, 20);
    println!("rect4 原始尺寸: {}x{}", rect4.width, rect4.height);
    rect4.set_dimensions(25, 30);
    println!("rect4 修改后尺寸: {}x{}", rect4.width, rect4.height);

    // 以 Debug 方式打印结构体
    println!("Debug 方式打印: {:?}", rect1);
    // 美化打印
    println!("美化打印: {:#?}", rect1);
    // Display 方式打印
    println!("Display 方式打印: {}", rect1);

    // dbg 宏控制台错误流输出
    // 调用 dbg! 宏会打印到标准错误控制台流（stderr），而不是 println!，后者会打印到标准输出控制台流（stdout）
    print!("dbg 方式打印: ");
    dbg!(&rect1);
}
